/*
 * Copyright (C) 2011-2021 Intel Corporation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   * Neither the name of Intel Corporation nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */


#include <sl_workers.h>
#include <sl_uswitchless.h>
#include <sl_once.h>
#include <sl_debug.h>
#include <sl_atomic.h>
#include <sl_init.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "sgx_trts.h"
#include "trts_internal_types.h"
#include <sl_fcall_mngr_common.h>

/*=========================================================================
 * Retrieve ECall table
 *========================================================================*/


extern const ecall_table_t g_ecall_table;
//
// create swtichless call table the same size as generated ECALLs table
// for every function in global ecall table check if it can be invoked by "switchless" call,
// if so, copy the function pointer, otherwise set to NULL
// 
static sl_call_table_t* __attribute__((optimize("O0"))) new_ecall_table(void) 
{
    uint32_t num_ecalls = (uint32_t)g_ecall_table.nr_ecall;

    size_t table_size = sizeof(sl_call_table_t)
                      + sizeof(sl_call_func_t) * num_ecalls;
    
    // fcall_table never gets freed. no global termination hooks defined
    sl_call_table_t* fcall_table = (sl_call_table_t*)malloc(table_size);
    if (fcall_table == NULL) 
        return NULL;

    fcall_table->size = num_ecalls;

    // go over global ecall table generated by edgr8r, and copy only switchless public function pointers 
    for (uint32_t fi = 0; fi < num_ecalls; fi++) 
    {
        const ecall_addr_t* ecall_info = &g_ecall_table.ecall_table[fi];
        fcall_table->funcs[fi] = ((ecall_info->is_switchless == 1) && (ecall_info->is_priv == 0)) ? 
                                 (sl_call_func_t)ecall_info->ecall_addr : NULL;
    }

    return fcall_table;
}

/*=========================================================================
 * Initialization
 *========================================================================*/

static struct sl_call_mngr g_ecall_mngr;
static sl_once_t g_init_ecall_mngr_done = SL_ONCE_INITIALIZER;

// initialize enclave's ecall manager
static uint64_t init_tswitchless_ecall_mngr(void* param)
{
    (void)param;
    if (g_uswitchless_handle == NULL) return EINVAL;

    // g_uswitchless_handle is checked in sl_init_switchless()
    struct sl_call_mngr* mngr_u = &g_uswitchless_handle->us_ecall_mngr;

    // clone ecall manager structure allocated outside enclave, performing all the relevant checks
    uint32_t ret = sl_call_mngr_clone(&g_ecall_mngr, mngr_u);
    if (ret != 0) 
		return ret;

    // abort in case of bogus initialization
    PANIC_ON(sl_call_mngr_get_type(&g_ecall_mngr) != SL_TYPE_ECALL);

    // allocate switchless ecalls table inside the enclave
    sl_call_table_t* ecall_table = new_ecall_table();
    
    if (ecall_table == NULL) 
		return ENOMEM;

    sl_call_mngr_register_calls(&g_ecall_mngr, ecall_table);

    return 0;
}

/*=========================================================================
 * Process Fast ECalls
 *========================================================================*/

/* The builtin ECMD_RUN_SWITCHLESS_TWORKER ECall calls this function eventually */
// This is a worker's thread function, which polls the event_lines bitmap for incoming
// switchless ECALLs requests
sgx_status_t sl_run_switchless_tworker()
{
    if (sl_call_once(&g_init_ecall_mngr_done, init_tswitchless_ecall_mngr, NULL))
        return SGX_ERROR_UNEXPECTED;
      
    // g_uswitchels_handle pointer is checked in sl_init_switchless() function 
    uint64_t max_retries = g_uswitchless_handle->us_config.retries_before_sleep;
    uint64_t retries = 0;

    while (retries < max_retries)
    {
        if (sgx_is_enclave_crashed())
            return SGX_ERROR_ENCLAVE_CRASHED;

        // g_ecall_mngr is a struct in trusted memory
        if (sl_call_mngr_process(&g_ecall_mngr) == 0)
        {
            if (g_uswitchless_handle->us_should_stop)
                break;

            retries++;
            asm_pause();
        }
        else
        {
            retries = 0;
        }
    }

    /* Return when the worker is being idle for some time */
    return SGX_SUCCESS;
}
